<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>充电宝租借系统 - 多柜机管理</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="./qrcode.js"></script>
    <style>
        * { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Microsoft YaHei', sans-serif; }
        body { background-color: #f5f7fa; color: #333; padding: 20px; }
        .container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); overflow: hidden; }
        .header { background: linear-gradient(135deg, #3498db, #2c3e50); color: white; padding: 20px; text-align: center; }
        .header h1 { margin-bottom: 10px; font-size: 24px; }
        .status-bar { display: flex; justify-content: space-between; background: #f1f2f6; padding: 10px 20px; font-size: 14px; }
        .status-connected { color: #2ecc71; font-weight: bold; }
        .status-disconnected { color: #e74c3c; font-weight: bold; }
        .device-selector { padding: 15px 20px; background: #eaf6ff; border-bottom: 1px solid #ddd; }
        .device-selector select { padding: 8px 15px; border-radius: 4px; border: 1px solid #ccc; width: 200px; }
        .main-content { padding: 20px; display: grid; grid-template-columns: 3fr 1fr; gap: 20px; }
        @media (max-width: 968px) { .main-content { grid-template-columns: 1fr; } }
        .slots-container { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; }
        .slot { background: #ecf0f1; border-radius: 8px; padding: 15px 10px; text-align: center; transition: all 0.3s ease; cursor: pointer; min-height: 120px; display: flex; flex-direction: column; justify-content: center; align-items: center; }
        .slot.available { background: #d5f5e3; border: 2px solid #2ecc71; }
        .slot.occupied { background: #fadbd8; border: 2px solid #e74c3c; }
        .slot.empty { background: #f2f3f4; border: 2px dashed #bdc3c7; cursor: not-allowed; }
        .slot-id { font-weight: bold; margin-bottom: 8px; font-size: 16px; }
        .slot-status { font-size: 14px; padding: 4px 8px; border-radius: 4px; margin-top: 8px; }
        .action-panel { background: #f8f9fa; border-radius: 8px; padding: 20px; display: flex; flex-direction: column; gap: 15px; }
        .action-btn { padding: 12px; border: none; border-radius: 6px; font-weight: bold; cursor: pointer; transition: all 0.2s ease; }
        .action-btn.report { background: #f39c12; color: white; }
        .action-btn.report:hover { background: #e67e22; }
        .action-btn.scan { background: #3498db; color: white; }
        .action-btn.scan:hover { background: #2980b9; }

        /* 改造后的设备信息卡片 */
        .device-info {
            background: #ffffff;
            border-radius: 12px;
            padding: 20px;
            text-align: center;
            box-shadow: 0 4px 12px rgba(0,0,0,0.08);
            transition: transform 0.2s ease, box-shadow 0.2s ease;
        }
        .device-info:hover {
            transform: translateY(-3px);
            box-shadow: 0 6px 18px rgba(0,0,0,0.12);
        }
        .device-info h3 {
            font-size: 16px;
            color: #2c3e50;
            margin-bottom: 10px;
        }
        .device-info p {
            margin: 5px 0;
            font-size: 14px;
            color: #34495e;
        }
        .device-qr-wrapper {
            margin: 15px auto;
            display: inline-block;
            padding: 12px;
            background: #f9f9f9;
            border-radius: 12px;
            border: 1px solid #ddd;
        }
        .device-info img,
        .device-info canvas {
            max-width: 120px !important;
            height: auto;
        }
        .device-info .qr-tip {
            margin-top: 8px;
            font-size: 13px;
            color: #7f8c8d;
        }

        .log-container { padding: 20px; border-top: 1px solid #eee; }
        .log-container h3 { margin-bottom: 10px; color: #2c3e50; }
        #content { width: 100%; height: 200px; padding: 12px; border: 1px solid #ddd; border-radius: 6px; background: #f8f9fa; font-family: monospace; font-size: 14px; resize: vertical; }
        .connection-info { background: #f8f9fa; padding: 15px; border-radius: 8px; margin-top: 20px; font-size: 14px; }
        .connection-info h3 { margin-bottom: 10px; color: #2c3e50; }
        .info-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
        .info-item { display: flex; justify-content: space-between; border-bottom: 1px dashed #ddd; padding: 5px 0; }
        .device-stats { display: flex; justify-content: space-around; margin: 15px 0; padding: 10px; background: #e8f4fc; border-radius: 8px; }
        .stat-item { text-align: center; }
        .stat-value { font-size: 24px; font-weight: bold; color: #3498db; }
        .stat-label { font-size: 14px; color: #7f8c8d; }
        .error-message { color: #e74c3c; padding: 10px; background: #fadbd8; border-radius: 4px; margin: 10px 0; display: none; }
        .loading { text-align: center; padding: 20px; color: #7f8c8d; }
    </style>
</head>
<body>
<div class="container">
    <div class="header">
        <h1>充电宝租借系统</h1>
        <p>多柜机管理系统</p>
    </div>

    <div class="status-bar">
        <div>状态: <span id="connection-status" class="status-disconnected">未连接</span></div>
        <div>时间: <span id="current-time"></span></div>
    </div>

    <div class="device-selector">
        <label for="device-select">选择柜机: </label>
        <select id="device-select">
            <option value="">-- 请选择柜机 --</option>
        </select>
    </div>

    <div id="error-message" class="error-message"></div>

    <div class="device-stats">
        <div class="stat-item">
            <div class="stat-value" id="total-slots">0</div>
            <div class="stat-label">总槽位</div>
        </div>
        <div class="stat-item">
            <div class="stat-value" id="available-slots">0</div>
            <div class="stat-label">可用槽位</div>
        </div>
        <div class="stat-item">
            <div class="stat-value" id="occupied-slots">0</div>
            <div class="stat-label">已占用</div>
        </div>
        <div class="stat-item">
            <div class="stat-value" id="empty-slots">0</div>
            <div class="stat-label">空槽位</div>
        </div>
    </div>

    <div class="main-content">
        <div class="slots-container">
            <div class="loading">请先选择一个柜机设备</div>
        </div>
        <div class="action-panel">
            <button class="action-btn report" id="report-property">上报属性测试</button>
            <button class="action-btn scan" id="simulate-scan">模拟扫码租借</button>

            <!-- 改造后的设备信息卡片 -->
            <div class="device-info">
                <h3>设备信息</h3>
                <p>编号：<span id="device-id">--</span></p>
                <p>名称：<span id="device-name">--</span></p>
                <div class="device-qr-wrapper">
                    <div id="device-qr"></div>
                </div>
                <p class="qr-tip">📱 扫描二维码租借充电宝</p>
            </div>
        </div>
    </div>

    <div class="connection-info">
        <h3>连接信息</h3>
        <div class="info-grid">
            <div class="info-item"><span>服务器:</span><span>192.168.200.130:8083</span></div>
            <div class="info-item"><span>客户端ID:</span><span id="client-id">--</span></div>
            <div class="info-item"><span>订阅主题:</span><span id="subscribe-topic">--</span></div>
            <div class="info-item"><span>发布主题:</span><span>/sys/powerBank/unlock</span></div>
        </div>
    </div>

    <div class="log-container">
        <h3>操作日志</h3>
        <label for="content"></label><textarea id="content" readonly></textarea>
    </div>
</div>

<script>
    $(function() {
        let currentCabinetId = null;
        let client = null;
        let qrCodeLoaded = false; // 标记QRCode库是否加载成功
        const urlPrex = `http://g27932ce.natappfree.cc`; // 服务器地址

        // 显示错误信息
        function showError(message) {
            $('#error-message').text(message).show();
            logMessage("错误: " + message);
        }

        // 隐藏错误信息
        function hideError() {
            $('#error-message').hide();
        }

        // 检查QRCode库是否加载成功
        function checkQRCodeLibrary() {
            if (typeof QRCode !== 'undefined' && typeof QRCode === 'function') {
                qrCodeLoaded = true;
                console.log('QRCode库加载成功');
            } else {
                console.error('QRCode库加载失败，将使用备用方案生成二维码');
                // 尝试重新加载库
                const script = document.createElement('script');
                script.src = './qrcode.js';
                script.onload = function() {
                    qrCodeLoaded = typeof QRCode !== 'undefined' && typeof QRCode === 'function';
                    console.log('重新加载QRCode库：', qrCodeLoaded ? '成功' : '失败');
                };
                document.head.appendChild(script);
            }
        }

        // 生成二维码
        function generateQRCode(text) {
            // 确保容器存在
            if (!document.getElementById("device-qr")) {
                console.error('二维码容器不存在');
                return;
            }

            // 清空容器
            $('#device-qr').empty().hide();
            if (!text || text === "未知") {
                $('#device-qr').html('<div class="fallback-qr">无设备编号</div>').show();
                return;
            }

            // 无论库是否加载成功，都先尝试使用备用方案确保显示
            const fallback = () => {
                const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=${encodeURIComponent(text)}`;
                const img = $('<img>')
                    .attr('src', qrUrl)
                    .attr('alt', '设备二维码')
                    .css({'max-width': '100px', 'max-height': '100px'})
                    .on('error', () => {
                        $('#device-qr').html('<div class="fallback-qr">二维码生成失败</div>');
                    });
                $('#device-qr').html(img).show();
            };

            // 如果库加载成功，先尝试使用库生成
            if (qrCodeLoaded) {
                try {
                    new QRCode(document.getElementById("device-qr"), {
                        text: text,
                        width: 100,
                        height: 100,
                        colorDark: "#000000",
                        colorLight: "#ffffff",
                        correctLevel: 2
                    });
                    $('#device-qr').show();
                } catch (error) {
                    console.error('QRCode库生成失败，使用备用方案:', error);
                    fallback();
                }
            } else {
                fallback();
            }
        }

        // 初始化设备列表
        function initDeviceSelector() {
            $.get(urlPrex + `/device/cabinet/devices`, function(res) {
                if (!res || res.code !== 200) {
                    showError("加载设备列表失败");
                    return;
                }
                const devices = res.data;
                const selector = $('#device-select');
                selector.empty();
                selector.append('<option value="">-- 请选择柜机 --</option>');

                devices.forEach(d => {
                    const cabinetId = d.cabinet_id || d.id;
                    if (cabinetId) {
                        selector.append(`<option value="${cabinetId}">${d.name}</option>`);
                    }
                });
            }).fail(function(xhr, status, error) {
                showError("请求设备列表失败: " + error);
            });

            $('#device-select').change(function() {
                const selectedId = $(this).val();
                if (!selectedId) {
                    resetDeviceData();
                    return;
                }

                currentCabinetId = selectedId;
                loadDeviceData(currentCabinetId);
                updateConnectionInfo();

                if (client && client.isConnected()) {
                    client.disconnect();
                }
                connectMQTT();
            });
        }

        // 重置设备数据
        function resetDeviceData() {
            $('.slots-container').html('<div class="loading">请先选择一个柜机设备</div>');
            $('#device-id').text('--');
            $('#device-name').text('--');
            $('#device-qr').hide();
            $('#total-slots').text('0');
            $('#available-slots').text('0');
            $('#occupied-slots').text('0');
            $('#empty-slots').text('0');
            $('#client-id').text('--');
            $('#subscribe-topic').text('--');

            if (client && client.isConnected()) {
                client.disconnect();
                $('#connection-status').removeClass('status-connected').addClass('status-disconnected').text('未连接');
            }
        }

        // 加载单个设备数据
        function loadDeviceData(cabinetId) {
            if (!cabinetId) {
                showError("无效的设备ID");
                return;
            }

            $.ajax({
                url: urlPrex + `/device/cabinet/devices/${cabinetId}`,
                method: "GET",
                dataType: "json",
                success: function(res) {
                    if (!res || res.code !== 200) {
                        showError("加载设备数据失败: " + (res ? res.msg : ""));
                        return;
                    }
                    hideError();

                    const device = res.data;
                    const cabinetNo = device.cabinetNo || "未知";
                    $('#device-id').text(cabinetNo);
                    $('#device-name').text(device.name || "未知");

                    // 生成二维码
                    setTimeout(() => {
                        generateQRCode(cabinetNo);
                    }, 100);

                    $('#total-slots').text(device.totalSlots || 0);
                    $('#available-slots').text(device.availableSlots || 0);
                    $('#occupied-slots').text(device.occupiedSlots || 0);
                    $('#empty-slots').text(device.emptySlots || 0);

                    initSlots(device.slots || []);
                },
                error: function(xhr, status, error) {
                    showError("加载设备数据失败: " + error);
                }
            });
        }

        // 渲染槽位
        function initSlots(slots) {
            const slotsContainer = $('.slots-container');
            slotsContainer.empty();

            if (!slots || slots.length === 0) {
                slotsContainer.html('<div class="error-message">该设备没有槽位数据</div>');
                return;
            }

            slots.forEach(slot => {
                if (!slot || !slot.id) return;

                const slotElement = $(`
            <div class="slot ${slot.status}" data-slot-id="${slot.id}" data-powerbank-id="${slot.powerBankId || ''}">
                <div class="slot-id">${slot.id}</div>
                <div>槽位 ${slot.id}</div>
                <div class="slot-status">${slot.status==='available'?'可用':slot.status==='occupied'?'已占用':'不可用'}</div>
            </div>
        `);

                if (slot.status === 'available' || slot.status === 'occupied') {
                    slotElement.click(() => handleSlotClick(slot.id, slot.status, slot.powerBankId));
                }

                slotsContainer.append(slotElement);
            });
        }

        // 槽位点击处理
        function handleSlotClick(slotId, status) {
            if (!currentCabinetId) {
                showError("请先选择有效的柜机设备");
                return;
            }

            const slot = $(`[data-slot-id="${slotId}"]`);
            if(status === 'available') {
                slot.removeClass('available').addClass('occupied').find('.slot-status').text('已租借');
                const mNo = generateRandomStr();
                const message = `mNo=${mNo}|cId=${currentCabinetId}|pNo=${slotId}|sNo=1|ety=100`;
                send("/sys/powerBank/unlock", message);
                updateSlotStats('rent', slotId);
            } else if(status === 'occupied') {
                slot.removeClass('occupied').addClass('available').find('.slot-status').text('可用');
                const mNo = generateRandomStr();
                const message = `mNo=${mNo}|cId=${currentCabinetId}|pNo=${slotId}|sNo=1|ety=90`;
                send("/sys/powerBank/connected", message);
                updateSlotStats('return', slotId);
            }
        }

        // 更新槽位统计
        function updateSlotStats(action, slotId) {
            if (!currentCabinetId) {
                showError("无法更新槽位统计：无效的设备ID");
                return;
            }

            $.ajax({
                url: urlPrex + `/device/cabinet/devices/${currentCabinetId}/slots/${slotId}/status`,
                method: "POST",
                contentType: "application/json",
                data: JSON.stringify({ action }),
                success: function(res) {
                    if (!res || res.code !== 200) {
                        showError("更新槽位状态失败");
                        return;
                    }
                    const updatedDevice = res.data;
                    $('#available-slots').text(updatedDevice.availableSlots || 0);
                    $('#occupied-slots').text(updatedDevice.occupiedSlots || 0);
                },
                error: function(xhr, status, error) {
                    showError("更新槽位统计失败: " + error);
                }
            });
        }

        // 更新时间
        function updateTime() {
            $('#current-time').text(new Date().toLocaleString());
        }
        setInterval(updateTime, 1000);
        updateTime();

        // 更新连接信息
        function updateConnectionInfo() {
            $('#client-id').text(currentCabinetId || "--");
            $('#subscribe-topic').text('/sys/scan/submit/' + (currentCabinetId || "--"));
        }

        // MQTT连接
        function connectMQTT() {
            if (!currentCabinetId) {
                showError("无法连接MQTT：无效的设备ID");
                return;
            }

            // 确保clientId是有效的字符串
            const clientId = String(currentCabinetId).substring(0, 23); // MQTT 3.1协议限制为23字符

            const hostname = '192.168.200.130';
            const port = 8083;
            const topic = '/sys/scan/submit/' + currentCabinetId;

            client = new Paho.MQTT.Client(hostname, port, clientId);

            const options = {
                timeout: 5,
                keepAliveInterval: 100,
                cleanSession: false,
                useSSL: false,
                onSuccess: onConnect,
                onFailure: function(e) {
                    logMessage("连接失败: " + e.errorMessage);
                    $('#connection-status').removeClass('status-connected').addClass('status-disconnected').text('连接失败');
                }
            };

            function onConnect() {
                logMessage("MQTT连接成功");
                $('#connection-status').removeClass('status-disconnected').addClass('status-connected').text('已连接');
                client.subscribe(topic);
                logMessage("已订阅主题: " + topic);
            }

            client.onConnectionLost = function(responseObject) {
                if(responseObject.errorCode !== 0){
                    logMessage("连接断开: " + responseObject.errorMessage);
                    $('#connection-status').removeClass('status-connected').addClass('status-disconnected').text('未连接');
                }
            };

            client.onMessageArrived = function(message) {
                logMessage("收到消息: " + message.payloadString);
                $.get(urlPrex + `/device/cabinet/devices/${currentCabinetId}/slots/available`, function(res) {
                    if (!res || res.code !== 200) {
                        showError("获取可用槽位失败");
                        return;
                    }
                    const availableSlot = res.data;
                    if (availableSlot) {
                        const mNo = generateRandomStr();
                        const responseMessage = `mNo=${mNo}|cNo=${currentCabinetId}|pNo=${availableSlot.id}|sNo=1|ety=100`;
                        send("/sys/powerBank/unlock", responseMessage);

                        const slotElement = $(`[data-slot-id="${availableSlot.id}"]`);
                        slotElement.removeClass('available').addClass('occupied');
                        slotElement.find('.slot-status').text('已租借');
                    } else {
                        logMessage("没有可用槽位，无法租借");
                    }
                }).fail(function(xhr, status, error) {
                    showError("请求可用槽位失败: " + error);
                });
            };

            logMessage("正在连接MQTT服务器...");
            client.connect(options);
        }

        function send(topic,message) {
            if (!client || !client.isConnected()) {
                logMessage("错误: MQTT连接未就绪");
                return;
            }
            const msg = new Paho.MQTT.Message(message);
            msg.destinationName = topic;
            client.send(msg);
            logMessage("发送消息到 " + topic + ": " + message);
        }

        function logMessage(message) {
            const now = new Date();
            const timestamp = now.toLocaleTimeString();
            $('#content').val($('#content').val() + `[${timestamp}] ${message}\r\n`);
            $('#content').scrollTop($('#content')[0].scrollHeight);
        }

        function generateRandomStr() {
            return 'mq' + (Math.floor(Math.random() * 90000000) + 10000000);
        }

        $('#report-property').click(function() {
            if (!currentCabinetId) {
                showError("请先选择有效的柜机设备");
                return;
            }

            $.get(urlPrex + `/device/cabinet/devices/${currentCabinetId}/slots/occupied`, function(res) {
                if (!res || res.code !== 200) {
                    showError("获取已占用槽位失败");
                    return;
                }
                const occupiedSlots = res.data;
                if (occupiedSlots && occupiedSlots.length > 0) {
                    const slot = occupiedSlots[0];
                    const mNo = generateRandomStr();
                    const message = `mNo=${mNo}|cNo=${currentCabinetId}|pNo=${slot.id}|sNo=1|ety=100`;
                    send("/sys/property/post", message);
                } else {
                    alert('当前没有已占用的充电宝，无法上报属性');
                }
            }).fail(function(xhr, status, error) {
                showError("请求已占用槽位失败: " + error);
            });
        });

        $('#simulate-scan').click(function() {
            if (!currentCabinetId) {
                showError("请先选择有效的柜机设备");
                return;
            }

            const mockMessage = { destinationName: "/sys/scan/submit/" + currentCabinetId, payloadString: "模拟扫码租借请求" };
            if(client && client.onMessageArrived) client.onMessageArrived(mockMessage);
        });

        checkQRCodeLibrary();
        initDeviceSelector();
    })

</script>

</body>
</html>